<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/**
 * @property Threads $threads
 */
class Demo_basic extends CI_Controller
{

	private static $server;
	private static $client;

	public function __construct()
	{
		parent::__construct();
		self::$server = websocket_server_open();
		$this->load->model('demo/threads');
	}

	public function index()
	{
		echo "server " . self::$server->host . ':' . self::$server->port . " is running" . PHP_EOL;

		self::$client = $this->threads->model('client','swoole');
		self::$client->init(); // 创建一个用于存储扫码页面参数信息的连接池

		self::$server->on('open', array($this, 'open'));

		self::$server->on('message', array($this, 'message'));

		self::$server->on('close', array($this, 'close'));

		//self::$server->on('task', array($this, 'task'));

		//self::$server->on('finish', array($this, 'finish'));

		self::$server->start();
	}

	/**
	 * 当WebSocket客户端与服务器建立连接并完成握手后会回调此方法
	 *
	 * @param swoole_websocket_server $server swoole_websocket_server 对象
	 * @param swoole_http_request $request Http请求对象，包含了客户端发来的握手请求信息
	 * @return void
	 */
	function open(swoole_websocket_server $server, swoole_http_request $request)
	{
		echo "server#{$server->worker_pid}: handshake success with client#{$request->fd} at " . date('Y-m-d H:i:s') . PHP_EOL;
	}

	/**
	 * 当服务器收到来自客户端的数据帧时会回调此方法
	 *
	 * @param swoole_websocket_server $server swoole_websocket_server 对象
	 * @param swoole_websocket_frame $frame 包含了客户端发来的数据帧信息，共有四个属性：
	 * $frame->fd，客户端的socket id，使用$server->push推送数据时需要用到
	 * $frame->data，数据内容，可以是文本内容也可以是二进制数据，可以通过opcode的值来判断
	 * $frame->opcode，WebSocket的OpCode类型，可以参考WebSocket协议标准文档
	 * $frame->finish， 表示数据帧是否完整，一个WebSocket请求可能会分成多个数据帧进行发送
	 * @return void
	 */
	function message(swoole_websocket_server $server, swoole_websocket_frame $frame)
	{
		$data = json_decode($frame->data);
		//$info = $server->getClientInfo($frame->fd);
		if (!empty($data->method)) {
			switch ($data->method) {
				case 'shake_hands':
					// 握手（扫码页发起）
					if (!empty($data->code)) {
						$server->push($frame->fd, json_encode(array('method' => $data->method, 'code' => encrypt(decrypt($data->code)), 'status' => 0)));
					} else {
						$server->push($frame->fd, json_encode(array('status' => 21004))); // 握手 code 为空
						echo "server#{$server->worker_pid}: error 21004" . PHP_EOL;
					}
					break;
				case 'register':
					// 注册（扫码页发起）
					self::$client->create($frame->fd,$data->client_id);
					$server->push($frame->fd, json_encode(array('method' => $data->method, 'status' => 0)));
					break;
				case 'scan':
					// 扫描（手机页发起）
					if($table = self::$client->get($data->client_id)){
						if(self::$client->is_scanned($data->client_id, $frame->fd)){
							// 该页面已经被扫描过
							$static = 20003;
						}elseif(self::$client->is_expired($data->client_id)){
							// 扫码页已过期
							echo "server#{$server->worker_pid}: client#" . $frame->fd . " qrcode has failed" . PHP_EOL;
							$static = 20002;
						}else{
							// 扫码页访问成功
							self::$client->scan($data->client_id, $frame->fd); // 标记该二维码已被成功扫过
							$server->push($table['fd'], json_encode(array('method' => $data->method, 'status' => 0))); // 向扫描页回执一条成功的消息
							$static = 0;
						}
					}else{
						// 扫码页被关闭或地址无效
						echo "server#{$server->worker_pid}: qrcode client is not found" . PHP_EOL;
						$static = 20001;
					}
					$server->push($frame->fd, json_encode(array('method' => $data->method, 'status' => $static)));
					break;
				case 'login':
					// 登录（手机页发起）
					if (!self::$client->is_scanned($data->client_id, $frame->fd)) {
						$table = self::$client->get($data->client_id);
						$server->push($table['fd'], json_encode(array('method' => $data->method, 'nickname' => $data->nickname, 'status' => 0))); // 向扫描页回执一条成功的消息
						$static = 0;
						//$server->task('do task'); // 这里模拟执行了一个 task（如果想要执行 task，需要首先设定 task_worker_num）
						//$server->swoole_table->del($client_id);
					} else {
						// 扫码页被关闭或地址无效
						echo "server#{$server->worker_pid}: client#" . $frame->fd . " is not found" . PHP_EOL;
						$static = 20001;
					}
					$server->push($frame->fd, json_encode(array('method' => $data->method, 'status' => $static)));
					break;
				default:
					$server->push($frame->fd, json_encode(array('status' => 21003))); // 无效的请求方式
					echo "server#{$server->worker_pid}: error 21003" . PHP_EOL;
			}
		} else {
			$server->push($frame->fd, json_encode(array('status' => 21003))); // 无效的请求方式
			echo "server#{$server->worker_pid}: error 21003" . PHP_EOL;
		}
		echo "server#{$server->worker_pid}: receive from client#{$frame->fd}:{$frame->data}|opcode:{$frame->opcode}|finish:{$frame->finish}" . PHP_EOL;
	}

	/**
	 * 当WebSocket客户端关闭时回调此方法
	 *
	 * @param swoole_websocket_server $server swoole_websocket_server 对象
	 * @param int $fd 客户端连接的标识符，在Server程序中是唯一的
	 * @return void
	 */
	function close(swoole_websocket_server $server, $fd)
	{
		echo "server#{$server->worker_pid}: client#{$fd} has been closed at " . date('Y-m-d H:i:s') . PHP_EOL;
		self::$client->close($fd);
	}

	/**
	 * 投递一个异步任务到task_worker池中。此方法是非阻塞的，执行完毕会立即返回
	 *
	 * @param swoole_websocket_server $server swoole_websocket_server 对象
	 * @param int $task_id task 任务的 ID
	 * @param int $src_worker_id 来自于哪个worker进程
	 * @param mixed $data 执行 task 时传入的内容
	 * @return void
	 */
	function task(swoole_websocket_server $server, $task_id, $src_worker_id, $data)
	{
		echo "server#{$server->worker_pid}: task start at task_id:{$task_id}|src_worker_id:{$src_worker_id}|data:{$data}" . PHP_EOL;
		$server->finish("response");
	}

	/**
	 * 用于在task进程中通知worker进程，投递的任务已完成。此方法可以传递结果数据给worker进程
	 *
	 * @param swoole_websocket_server $server swoole_websocket_server 对象
	 * @param int $task_id 执行 task 的 ID
	 * @param mixed $result 调用 finish 时传入的内容（一般是 task 任务的结果）
	 * @return void
	 */
	function finish(swoole_websocket_server $server, $task_id, $result)
	{
		echo "server#{$server->worker_pid}: task finish at task_id:{$task_id}|result:{$result}" . PHP_EOL;
	}

}